﻿using System;
using System.Drawing;
using Emgu.CV;
using Emgu.CV.Structure;
using iMapa.Utils;

namespace iMapa.Backend
{
    public interface ISpotDetector
    {
        Point? getSpot(Image<Bgr, byte> frame);
    }

    public class SpotDetector:ISpotDetector
    {
        private RunningAverage ra;
        private AbsDiffFrame adf;
        private LastDetectedPoint ldp;
        private InteractMapGroup img;

        private int detectionSequenceCount;

        public SpotDetector()
        {
            this.ra = RunningAverage.getInstance();
            this.adf = AbsDiffFrame.getInstance();
            this.ldp = LastDetectedPoint.getInstance();
        }

        public Point? getSpot(Image<Bgr, byte> f)
        {
            if (f == null || img == null || img.CurrentDetected().Count==0 )
            {
                return null;
            }
            Image<Gray, Byte> frame = f.Convert<Gray, Byte>();
            Image<Gray, float> avg = ra.getAverage() ;
            Image<Gray, float> diff;

            diff=adf.computeAbsDiffFrame(frame);
            diff._And(img.GetDetectionMask(diff.Size));
            
            double[] minval, maxval;
            Point[] minP, maxP;
            diff.MinMax(out minval, out maxval, out minP, out maxP);

            for (int j = 0; j < minP.Length; j++)
            {
                Point p = maxP[j];
                double d = maxval[j];
                if (diff.Data[p.Y, p.X, 0] > 30 && isDarkAround(diff, p.X, p.Y, 5) && frame.Data[p.Y, p.X, 0] > avg.Data[p.Y, p.X, 0])
                {
                    IncDetectionSequenceCount();
                    if (frame.Data[p.Y, p.X, 0] > 60)
                    {

                        if (IsDetectionInSequence())
                        {
                            return p;
                        }
                    }
                }
                else
                {
                    avg = ra.addImageToAverage(frame);
                    DecrDetectionSequenceCount();
                }
            }           
            return null;
        }

        private bool isDarkAround(Image<Gray, float> source, int x, int y, double threshold)
        {
            int width = source.Width;
            int heigh = source.Height;
            Rectangle rect= GetPointSurroundingROI(x, y, width, heigh, 10);
            PointF[] pts = new PointF[] { 
                        new PointF(rect.Left, rect.Bottom),
                        new PointF(rect.Right, rect.Bottom),
                        new PointF(rect.Right, rect.Top),
                        new PointF(rect.Left, rect.Top)};
            source.FillConvexPoly(Array.ConvertAll<PointF, Point>(pts, Point.Round), new Gray(0));
            source.ROI = GetPointSurroundingROI(x, y, width, heigh, 50);
            Gray average;
            MCvScalar deviation;
            source.AvgSdv(out average, out deviation);
            source.ROI = Rectangle.Empty;
            if (average.Intensity < threshold)
                return true;
            return false;
        }

        private Rectangle GetPointSurroundingROI(int x, int y, int width, int height, int area)
        {
            return new Rectangle(val(x - area, width), val(y - area, height), val(x + area, width) - val(x - area, width), val(y + area, height) - val(y - area, height));
        }

        private int val(int a, int maxa)
        {
            if (a < 0)
                a = 0;
            if (a > maxa - 1)
                a = maxa - 1;
            return a;
        }

        private void IncDetectionSequenceCount()
        {
            detectionSequenceCount = detectionSequenceCount >= 5 ? 5 : detectionSequenceCount + 1;
        }

        private void DecrDetectionSequenceCount()
        {
            detectionSequenceCount = detectionSequenceCount <= 0 ? 0 : detectionSequenceCount - 1;
        }

        private bool IsDetectionInSequence()
        {
            return detectionSequenceCount >= 3 ? true : false;
        }

        public InteractMapGroup Img
        {
            get { return img; }
            set { img = value; }
        }
    }
}
